/* 
 * File:   ParStack.h
 * Author: tomas
 *
 * Created on May 3, 2009, 5:09 PM
 */

#ifndef _PARSTACK_H
#define	_PARSTACK_H

#include <stack>
#include <list>

#include "stackrecord.h"
#include "interfaces/instancei.h"

struct AvailableWorkInfo {
    unsigned workUnitsCount; // pocet zaznamu, ktere lze poslat dale

    // toto se ted nepouziva!!
    unsigned workWeight; // "velikost" prace pro odeslani

    AvailableWorkInfo(unsigned _wuCount, unsigned _workWeight)
		: workUnitsCount(_wuCount), workWeight(_workWeight) {
    }
};


class ParStack : public std::stack<StackRecord, std::list<StackRecord> > {
private:

    typedef std::stack<StackRecord> Base;

    typedef std::list<StackRecord> Container;

	//Pointer na instanci se zadanim
    InstanceI* pInstance;

public:
    typedef std::vector<StackRecord> workUnit_t;

    ParStack(InstanceI* i) : pInstance(i) {
    }

	//Konstanta, ktera zatim urcuje minimalni velikost posilatelne prace,
	//predpokladame, ze mensi pocet unitu nema cenu posilat.
	//TODO:Pokud bude treba, navrhnout dynamicke vyvazovani.
    static const unsigned MIN_RECORD_COUNT = 4;

	//////////////////////////////////////////////////////////////////////////
	// Funkce rozdeli zasobnik, vraci nejvyse zadany pocet kontejneru se stavy zasobniku,
	// jejichz pocet je ale omezen dostupnou praci, tedy muze vratit mene nez bylo pozadovano.
	// Zaroven je zaruceno, ze po rozdeleni bude lokalne dostupna nejaka prace, pokud byla 
	// pred rozdelenim.
	//
    // reqCount - pocet zadatelu o praci
    // maxDepth - maximalni hloubka, ktera se jeste bude delit
	// returns kontejner kontejneru se stavy k prozkoumani.
    std::vector<workUnit_t> divide(unsigned reqCount, unsigned maxDepth) {

		//Zjistime si info o dostupne praci (napr. pocet posilatelnych stavu)
        AvailableWorkInfo wi = getAvailableWorkInfo(maxDepth);
		
		//Staticky zjistime kolik je max. pocet dilu, na kt. lze zasobnik rozdelit,
		//pocitano vcetne jeho samotneho (tj.vcetne lokalniho zasobniku).
        unsigned maxChunkCount = wi.workUnitsCount / MIN_RECORD_COUNT;

		//Spocitat pocet splnenych pozadavku behem tohoto volani fce.
		//Prave tolik se vytvori workUnitu, alias jednotek prace.
		//Do tohoto poctu nepocitam lokalni zasobnik, nebot tyto stavy se nikam presouvat nebudou.
        unsigned fulfiledReqCount = ParStack::min<unsigned>(maxChunkCount - 1, reqCount);

		//Vystupni promenna
        std::vector<workUnit_t> workUnits(fulfiledReqCount);

        //Citac skupiny, do ktere se zaradi workUnit
		unsigned groupIndex = 0;

        for (Container::iterator i = c.begin(); i != c.end(); )
		{
            if (i->depth <= maxDepth) //kdyz je stav v hloubce, kde se jeste da delit
			{
				//nejvyssi index odpovida lokalnimu zasobniku
				if (groupIndex != fulfiledReqCount)
				{
					workUnits[groupIndex].push_back(*i);
					i = c.erase(i);
				}
				else //jinak stav zustane na lokalnim zasobniku
				{
					++i;
				}
				//citame modulo pocet zadosti + 1, tj.vcetne lok. zasobniku
				//platne indexy do vectoru jsou ovsem jen do fulfiledReqCount-1, index od 0
				incrementModulo(groupIndex, fulfiledReqCount + 1);

            }
			else //stav je hloubeji nez maxDepth
			{
				++i;
			}
		}

        return workUnits;
    }

private:
	
	template<typename T>
	static T min(const T& x, const T& y)
	{
		return (x<=y)? x : y ;
	}

	template<typename T>
	static void incrementModulo(T& n, const T mod)
	{
		++n;
		if (n==mod)
		{
			n = 0;
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Funkce vraci informace o lokalne dostupne praci na zasobniku.
	// Params:
	// maxDepth - stavy pod touto hladinou se uz nepocitaji za delitelnou praci.
    AvailableWorkInfo getAvailableWorkInfo(size_t maxDepth) {
        unsigned recCount = 0;
        unsigned workWeight = 0;

        for (Container::iterator i = c.begin(); i != c.end(); ++i) {
            if (i->depth <= maxDepth) {
                recCount++;
            }
        }
        return AvailableWorkInfo(recCount, workWeight);
    }





    //    unsigned computeWeight(unsigned q, unsigned depth) {
    //
    //        return pow( q - depth, 8 );
    //
    //    }


};



#endif	/* _PARSTACK_H */



